package com.af.security.jwt.token;

import com.af.security.auth.login.service.AfUserDetailsService;
import com.af.security.auth.user.detail.AfUserDetails;
import com.af.security.jwt.JwtAuthenticationException;
import com.af.security.property.JwtProperties;
import com.fasterxml.jackson.databind.ObjectMapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.MacSigner;
import org.springframework.security.jwt.crypto.sign.SignatureVerifier;
import org.springframework.security.jwt.crypto.sign.Signer;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author : zhenyun.su
 * @comment : Jwt工具， 用于创建token ，使用spring-security-jwt框架来实现 (推荐使用)
 * 参考：JwtAccessTokenConverter， 来自 spring-security-oauth2
 * 优化： 将token， authentication 保存到缓存中，然后从缓存获取
 * @since : 2020/5/8
 *
 */

public class DefaultJwtTokenService implements JwtTokenService {
    private static final Logger logger = LoggerFactory.getLogger(DefaultJwtTokenService.class);
    private Signer signer;
    private SignatureVerifier verifier;

    @Autowired
    private JwtProperties jwtProperties;
    @Autowired
    private ObjectMapper mapper;
    @Autowired
    private AfUserDetailsService userDetailsService;

    @Override
    public String getHeaderKey(){
        return jwtProperties.getHeaderKey();
    }
    @Override
    public String getTokenPrefix(){
        return jwtProperties.getTokenPrefix();
    }

    public void setSigningKey(String key) {
        this.signer = new MacSigner(key);
        this.verifier = new MacSigner(key);
    }

    @Override
    public String createToken(Authentication authentication) throws JwtAuthenticationException {
        logger.debug("createToken, start");
        try{
            //设置自定义属性
            Map<String, Object> chains = new HashMap<>();
            List<String> authorityList = new ArrayList<>();
            for (GrantedAuthority authority : authentication.getAuthorities()) {
                authorityList.add(authority.getAuthority());
            }

            Long currentTime = System.currentTimeMillis()/1000L;
            Long exp = currentTime + jwtProperties.getExpiration();

            // chains.put("authorities", mapper.writeValueAsString(authorityList));
            chains.put("organization", "daphne");
            chains.put("exp", exp);
            chains.put("jti", authentication.getName());
            chains.put("sub", authentication.getName());
            chains.put("iat", currentTime);
            chains.put("iss", "sys");
            String content = mapper.writeValueAsString(chains);
            this.setSigningKey(jwtProperties.getSecret());
            String token = JwtHelper.encode(content, this.signer).getEncoded();
            return token;
        }catch (Exception e){
            throw new JwtAuthenticationException("createToken failed "+e.getMessage());
        }
    }

    @Override
    public String getTokenValue(HttpServletRequest request) throws JwtAuthenticationException {

        String jwtToken = request.getHeader(getHeaderKey());
        if (jwtToken == null) {
            return null;
        } else {
            jwtToken = jwtToken.trim();
            if (ObjectUtils.isEmpty(getTokenPrefix())){
                return jwtToken;
            }
            if (!StringUtils.startsWithIgnoreCase(jwtToken, getTokenPrefix())) {
                throw new JwtAuthenticationException("Token prefix cannot be empty");
            } else if (jwtToken.equalsIgnoreCase(getTokenPrefix())) {
                throw new JwtAuthenticationException("Empty authentication token");
            } else {
                return jwtToken.substring(getTokenPrefix().length()+1);
            }
        }
    }

    /*
    x3, jwt.payload
    {
      "branchId": "100601",
      "userIconUrl": null,
      "adminType": "localSystem",
      "dbName": null,
      "companyName": "日播",
      "language": "zh-cn",
      "sessionId": null,
      "userName": "苏震云_测试",
      "userId": "RB210212",
      "userKey": "ZYCG",
      "companyId": "ribo",
      "appType": "pc",
      "erpId": null,
      "userType": 1,
      "exp": 1655984767
    }
    * */
    @Override
    public Authentication getAuthentication(String token) throws JwtAuthenticationException {
        try {
            Jwt jwt = JwtHelper.decode(token);
            // Jwt jwt = JwtHelper.dedecodeAndVerify(token, this.verifier);
            String claimsStr = jwt.getClaims();
            logger.debug("getAuthentication, start");
            Map<String, Object> claims = mapper.readValue(claimsStr, Map.class);

            Integer exp= (Integer)claims.get("exp");
            if (exp < System.currentTimeMillis()/1000){
                throw new JwtAuthenticationException("The token has expired");
            }

            String userId = null;
            if (claims.get("sub") != null) {
                userId = claims.get("sub").toString();
            }
            if (userId ==  null){
                throw new JwtAuthenticationException("userId is empty");
            }

            AfUserDetails userDetails = userDetailsService.loadUserByUsername(userId);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, "", userDetails.getAuthorities());
            return authentication;
        } catch (Exception e) {
            throw new JwtAuthenticationException("af authentication, failed: "+e.getMessage());
        }

    }
}
